package its.opt.utils;

import its.model.ModelException;
import its.model.RID;
import its.model.environment.crossroad.Program;
import its.opt.program.EnvironmentBuilder;
import its.sim.SimulatorConfiguration;
import its.sim.ui.SimulatorFiles;
import its.sim.ui.dom.EnvironmentDOMParser;
import its.util.TimeUtils;
import its.util.Utils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import engine.Algorithm;
import engine.ObjectiveFunction;
import engine.individuals.TrafficNetIndividual;
import engine.individuals.util.IndividualsComparator;

public class OptimizationEvaluatorUtils {

	private final static Logger LOGGER = Logger
			.getLogger(OptimizationEvaluatorUtils.class.getName());
	
	public static <S extends TrafficNetIndividual<?>> void saveBestIndividual(
			String[] args, Algorithm<S> alg,
			ObjectiveFunction<S> objectiveFunction) {
		S best = Collections.max(alg.getPopulation().getIndividuals(),
				new IndividualsComparator<S>(objectiveFunction));

		Map<RID, Program> programsMap = packProgramsToMap(best.getPrograms());

		String storeFileNameBase = System.getProperty("hostname") + "-"
				+ TimeUtils.DATE_FORMAT.format(new Date()) + "best-individual";
		String propertyOutFile = System.getProperty("out.file");
		if (propertyOutFile != null && propertyOutFile.trim().length() > 0) {
			storeFileNameBase = propertyOutFile;
		}

		storeModel(storeFileNameBase, args, programsMap);
	}
	
	private static File storeModel(
			String storeFileNameBase, 
			String[] commandLineAgruments,
			Map<RID, Program> programs)
	{
		return storeModel(null, storeFileNameBase, commandLineAgruments, programs);
	}
	
	public static File storeModel(
			String storeDirName,
			String storeFileNameBase, 
			String[] commandLineAgruments,
			Map<RID, Program> programs)
	{
		File storageDir;
		if (storeDirName != null) {
			storageDir = OptimizationEvaluatorUtils.initDirectory(storeDirName);
		} else {
			storageDir = new File(System.getProperty("user.dir"));
		}

		SimulatorFiles simulationFiles = 
			OptimizationEvaluatorUtils
				.initializeSimulatorFiles(commandLineAgruments);

		
		File environmentFile = 
			OptimizationEvaluatorUtils
				.dumpEnvironment(
						storageDir,
						storeFileNameBase,
						// String.format("evotest-%02d",i),
						simulationFiles,
						programs);
		
		return environmentFile;
	}
	
	private static File dumpEnvironment(
			File dir,
			String name,
			SimulatorFiles conf,
			Map<RID, Program> programs)
	{
		File env_file = new File(dir, name + ".xml");
		File conf_file = new File(dir, name + ".conf");

		EnvironmentBuilder builder = new EnvironmentBuilder(env_file);
		for (Entry<RID, Program> entry : programs.entrySet()) {
			builder.setProgram(entry.getKey(), entry.getValue());
		}

		EnvironmentDOMParser parser = new EnvironmentDOMParser(builder);
		try {
			parser.parse(conf.getEnvironmentFile());
		} catch (Exception e) {
			e.printStackTrace();
		}
		builder.close();

		try {
			conf_file.createNewFile();
			FileWriter output = new FileWriter(conf_file);
			output.write("model = "
					+ Utils.repairWindowsPath(env_file.getAbsolutePath())
					+ "\n");
			output.write("generator = "
					+ Utils.repairWindowsPath(conf.getGeneratorFile()
							.getAbsolutePath()) + "\n");
			output.write("shapes = "
					+ Utils.repairWindowsPath(conf.getShapesFile()
							.getAbsolutePath()) + "\n");
			output.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return conf_file;
	}

	private static File initDirectory(String dirName)
	{
		final File dir = new File(dirName);
		if (dir.isDirectory()) {
			for (File f : dir.listFiles()) {
				f.delete();
			}
		} else if (dir.exists()) {
			dir.delete();
			dir.mkdir();
		} else {
			dir.mkdir();
		}
		return dir;
	}

	private static SimulatorFiles initializeSimulatorFiles(String[] args) {
		SimulatorFiles simulationFiles;
		try {
			simulationFiles = 
				new SimulatorFiles(
					new SimulatorConfiguration(args).getConfigFile());
		} catch (ModelException e) {
			e.printStackTrace();
			LOGGER.log(Level.SEVERE,
					"Model problem initializing simulator files.", e);
			throw new IllegalStateException(e.toString());
		} catch (IOException e) {
			e.printStackTrace();
			LOGGER.log(Level.SEVERE,
					"File problem initializing simulator files.", e);
			throw new IllegalStateException(e.toString());
		}
		return simulationFiles;
	}

	public static Map<RID, Program> packProgramsToMap(Program[] programArray)
	{
		Map<RID, Program> programs = new HashMap<RID, Program>(programArray.length);
		
		for (Program program : programArray)
		{
			programs.put(program.getCrossroadID(), program);
		}
		return programs;
	}
}
